/**
 * Copyright (C) 2005-2011 by Rivello Multimedia Consulting (RMC).                    
 * code [at] RivelloMultimediaConsulting [dot] com                                                  
 *                                                                      
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the      
 * "Software"), to deal in the Software without restriction, including  
 * without limitation the rights to use, copy, modify, merge, publish,  
 * distribute, sublicense, and#or sell copies of the Software, and to   
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:                                            
 *                                                                      
 * The above copyright notice and this permission notice shall be       
 * included in all copies or substantial portions of the Software.      
 *                                                                      
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,      
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF   
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR    
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.                                      
 */
// Marks the right margin of code *******************************************************************
package com.rmc.utils
{
	
	//--------------------------------------
	//  Imports
	//--------------------------------------
	import com.greensock.events.LoaderEvent;
	import com.greensock.loading.DataLoader;
	import com.greensock.loading.LoaderMax;
	import com.pblabs.engine.PBE;
	import com.rmc.data.types.Manufacturer;
	import com.rmc.data.types.OS;
	import com.rmc.data.types.PlayerType;
	import com.rmc.errors.SwitchStatementDefaultError;
	
	import flash.system.Capabilities;
	import flash.system.Security;
	
	
	//--------------------------------------
	//  Metadata
	//--------------------------------------
	
	//--------------------------------------
	//  Class
	//--------------------------------------
	/**
	 * <p>The <code>Environment</code> contains helper methods and properties about the 
	 * surrounding environment of the Flash content.</p>
	 * 
	 * <p>This includes things like; Is the swf residing online? Is the swf connected to the internet? Etc...</p>
	 * 
	 * <p>AUTHOR 		: Samuel Asher Rivello</p>
	 * <p>COMPANY 		: Rivello Multimedia Consulting</p>
	 * <p>CREATION DATE 	: Nov 17, 2010</p>
	 * 
	 * @example Here is a code example. 
	 *  
	 * <listing version="3.0">
	 * <p>Describe this code example.</p>
	 *
	 * </listing>
	 *
	 */
	public class Environment
	{		
		
		//--------------------------------------
		//  Properties
		//--------------------------------------
		//PUBLIC GETTER/SETTERS
		/**
		 * Determines if the check has been completed to 'know' internet connectivity
		 * 
		 */		
		public function get hasInitializedIsInternetAvailable () 						      : Boolean 	{ 	return _hasInitializedIsInternetAvailable_boolean; 		}
		public function set hasInitializedIsInternetAvailable (value : Boolean) 	: void 		{ 	_hasInitializedIsInternetAvailable_boolean = value; 		}
		private var _hasInitializedIsInternetAvailable_boolean : Boolean;
		
		//STATIC
		/**
		 * Determines if the check has been completed to 'know' internet connectivity
		 * 
		 * NOTE: Must be public so the static scope can reach it. Not really meant for end users to get at this INSTANCE variable. but that's ok.
		 * 
		 */		
		public function get isInternetAvailable () 						      : Boolean 	{ 	return _isInternetAvailable_boolean; 		}
		public function set isInternetAvailable (value : Boolean) 	: void 		{ 	_isInternetAvailable_boolean = value; 		}
		private var _isInternetAvailable_boolean : Boolean;
		
		
		//PUBLIC CONST
		/**
		 * Instance of the singleton class.
		 * 
		 * @private
		 * 
		 */
		protected static var _instance : Environment;
		
		//PRIVATE
		
		//PRIVATE STATIC
		/**
		 * Determines if the swf has connectivity to internet.
		 * 
		 */
		private static var isInitialized_boolean : Boolean;
		
		/**
		 * Part of a url that is offline.
		 * 
		 */
		private static const _FILE : String = "file://";
		
		/**
		 * When we want to see if the app has connectivity, use this 'always available (hopefully)' website
		 * 
		 */
		private static const _URL_FOR_CONNECTIVITY_TESTING : String =  "http://www.google.com"
		
		
		//--------------------------------------
		//  Constructor
		//--------------------------------------
		/**
		 * This is the constructor.
		 * 
		 */
		public function Environment(singletonEnforcer : SingletonEnforcer)
		{
			//SUPER
			
			//EVENTS
			
			//VARIABLES
			
			//PROPERTIES
			Environment.isInitialized_boolean = false;
			isInternetAvailable = false;
			var dummy_boolean : * = Environment.isInternetAvailable; //call once to load and update 'isInternetAvailable'
			
			//METHODS
			
		}
		
		
		
		
		//--------------------------------------
		//  Methods
		//--------------------------------------		
		//PUBLIC
		
		//PUBLIS STATIC
		/**
		 * Get an instance of the singleton class.
		 * 
		 * @return <code>Environment</code> The instance to return
		 *
		 */
		public static function getInstance() : Environment  
		{
			if (!_instance) {
				_instance = new Environment(new SingletonEnforcer() ); 
			}
			return _instance;
		}
		
		/**
		 * This is called one time. It is meant to call 'allowDomain' for any/all domains needed.
		 * This way each application doesn't need to do it manually.
		 * 
		 */
		public static function addAllAllowDomains() : void 
		{
			Security.allowDomain("*");
		}
		
		
		/**
		 * Return what type of environment (desktop, browser, etc...) we are in.
		 * 
		 * NOTE: Do not use this property to determine a capability 
		 * for a feature if a feature-specific boolean exists.
		 * 
		 * @return PlayerType
		 * 
		 */
		public static function get playerType() : PlayerType
		{
			var playerTypeName_str : String = Capabilities.playerType;
			var playerType : PlayerType;
			//
			switch (playerTypeName_str){
				case "ActiveX":
					playerType = PlayerType.ACTIVE_X;
					break;
				case "Desktop":
					playerType = PlayerType.DESKTOP;
					break;
				case "External":
					playerType = PlayerType.EXTERNAL;
					break;
				case "PlugIn":
					playerType = PlayerType.PLUG_IN;
					break;
				case "StandAlone":
					playerType = PlayerType.STAND_ALONE;
					break;
				default:
					throw new SwitchStatementDefaultError();
					break;
			}
			
			return playerType;
		}
		
		/**
		 * Return what type of device Manufacturer (Windows, Macintosh, etc...) we are in.
		 * 
		 * NOTE: Do not use this property to determine a capability 
		 * for a feature if a feature-specific boolean exists. 
		 * 
		 * @return Manufacturer
		 * 
		 */
		public static function get manufacturer() : Manufacturer
		{
			//Specifies the manufacturer of the AIR runtime, in the format "Adobe OSName".
			var manufacturer_str : String = Capabilities.manufacturer;
			var manufacturer : Manufacturer;
			//
			if 		(manufacturer_str.indexOf("Linux") != -1) {
				manufacturer = Manufacturer.LINUX;
			} else if  	(manufacturer_str.indexOf("VMware") != -1) {
				manufacturer = Manufacturer.BLACKBERRY;
			} else if  	(manufacturer_str.indexOf("iOS") != -1) {
				manufacturer = Manufacturer.IOS;
			} else if  	(manufacturer_str.indexOf("Macintosh") != -1) {
				manufacturer = Manufacturer.MACINTOSH;
			} else if  	(manufacturer_str.indexOf("Windows") != -1) {
				manufacturer = Manufacturer.WINDOWS;
			} else {
				throw new Error ("Could Not Detect Manufacturer. Please fix.");
			}
			
			return manufacturer;
		}
		
		
		/**
		 * Return what type of OS (Linux, BlackberryTabletOS, etc...)
		 * 
		 * NOTE: Do not use this property to determine a capability 
		 * for a feature if a feature-specific boolean exists.
		 * 
		 * @return OS
		 * 
		 */
		public static function get os() : OS
		{
			var os_str : String = Capabilities.os;
			var os : OS;
			//
			if 		(os_str.indexOf("Linux") != -1) {
				os = OS.LINUX;
			} else if  	(os_str.indexOf("Blackberry") != -1) {
				os = OS.BLACKBERRY_TABLET;
			} else if  	(os_str.indexOf("iPhone") != -1) {
				os = OS.IOS;
			} else if  	(os_str.indexOf("Mac OS") != -1) {
				os = OS.MAC_OS;
			} else if  	(os_str.indexOf("Windows") != -1) {
				os = OS.WINDOWS;
			} else {
				throw new Error ("Could Not Detect OS. Please fix.");
			}
			
			return os;
		}
		
		
		/**
		 * Checks if the player is in a browser (more or less).
		 * 
		 * NOTE: Do not use this property to determine a capability 
		 * for a feature if a feature-specific boolean exists.
		 * 
		 * @return Boolean
		 * 
		 */
		private static function get _isPlayerTypeWithinABrowser() : Boolean
		{
			return ((Environment.playerType == PlayerType.ACTIVE_X) || (Environment.playerType == PlayerType.PLUG_IN) );
		}
		
		/**
		 * Checks if the player is in AIR (more or less). 
		 * 
		 * NOTE: Do not use this property to determine a capability 
		 * for a feature if a feature-specific boolean exists.
		 * 
		 * @return Boolean
		 * 
		 */
		public static function get isPlayerTypeWithinAIR() : Boolean
		{
			return !_isPlayerTypeWithinABrowser;
		}
		
		/**
		 * Checks if the player is in AIR FOR MOBILE (more or less). 
		 * 
		 * NOTE: Do not use this property to determine a capability 
		 * for a feature if a feature-specific boolean exists.
		 * 
		 * @return Boolean
		 * 
		 */
		public static function get isPlayerTypeWithinAIRForMobile() : Boolean
		{
			var isPlayerTypeWithinAIRForMobile_boolean : Boolean;
			if (isPlayerTypeWithinAIR) {
				//DESKTOP?
				if (manufacturer		== Manufacturer.WINDOWS ||
					manufacturer		== Manufacturer.MACINTOSH) {
					isPlayerTypeWithinAIRForMobile_boolean = false;
					
				} else {
					//MOBILE!!!
					//MOBILE!!!
					//MOBILE!!!
					isPlayerTypeWithinAIRForMobile_boolean = true;
					
				}
			} else {
				isPlayerTypeWithinAIRForMobile_boolean = false;
			}
			
			return isPlayerTypeWithinAIRForMobile_boolean;
		}
		
		
		/**
		 * Shows a string that reflects the PlayerType configuration.
		 * 
		 * @return String, Returns Debug info about the player type. This may be displayed via Debugger.info();
		 * 
		 */
		public static function getPlayerTypeDebugMessage() : String
		{
			var playerTypeDebugMessage_str : String;
			//
			switch (Environment.playerType){
				case PlayerType.ACTIVE_X:
					playerTypeDebugMessage_str =   "an IE ActiveX";
					break;
				case PlayerType.DESKTOP:
					playerTypeDebugMessage_str =   "an Air app";
					break;
				case PlayerType.EXTERNAL:
					playerTypeDebugMessage_str =   "an external flash player";
					break;
				case PlayerType.PLUG_IN:
					playerTypeDebugMessage_str =   "a webpage in a non-IE browser";
					break;
				case PlayerType.STAND_ALONE:
					playerTypeDebugMessage_str =   "a standalone flash player";
					break;
				default:
					throw new SwitchStatementDefaultError();
					break;
			}
			
			return "Currently has playertype of " + playerTypeDebugMessage_str + ".";
		}
		
		
		/**
		 * Checks if the current swf is located online on a server('live') or 
		 * offline on a personal computer(development)
		 * 
		 * @return Boolean True means the file is located on a server('live')
		 * 
		 */
		public static function isFileOnline() : Boolean 
		{
			var loaderInfoURL_str : String = Environment.swfURL;
			var localMode2_boolean : Boolean = loaderInfoURL_str.indexOf(Environment._FILE) != -1;
			return !localMode2_boolean;
		}
		
		/**
		 * The full url of the currently running swf - whether online or offline.
		 * 
		 * TODO: Return only one time. THus use *no* contingency that is shown.
		 * 
		 * @return String of the URL
		 * 
		 */
		public static function get swfURL() : String 
		{
			if (RMCGlobals.STAGE) {
				return RMCGlobals.STAGE.loaderInfo.url;
			} else {
				return "null"; //not available yet
			}
		}
		
		/**
		 * Checks for a connection to the internet
		 * 
		 * NOTE: This returns false 100% until the <code>DataLoader</code> can make its first test call. 
		 * This takes time, but just milliseconds from Application startup time. Then the value is set. 
		 * 
		 * NOTE:  Because it is an asynchronous load to a sample script, unfortunately the value is not 
		 * *IMMEDIATELY* available upon startup. That is ok. The API is already careful not to call it 'too early', and 
		 * on/after the time MyYearbook.state == State.INITIALIZED (when the developer does most of their coding) it is readily available.
		 
		 * 
		 * @return Boolean True means there is internet connectivity
		 * 
		 */
		public static function get isInternetAvailable () : Boolean 
		{
			var isInternetAvailable_boolean : Boolean;
			if (Environment.isInitialized_boolean) {
				isInternetAvailable_boolean = getInstance().isInternetAvailable;
			} else {
				isInternetAvailable_boolean = false; //for now
				
				//TODO, PUT THIS URL IN A CONSTANT SOMEWHERE
				var internetAvailable_dataloader : DataLoader = new DataLoader(Environment._URL_FOR_CONNECTIVITY_TESTING);
				
				//
				internetAvailable_dataloader.addEventListener(LoaderEvent.IO_ERROR, Environment._onInternetAvailableIOError);
				internetAvailable_dataloader.addEventListener(LoaderEvent.ERROR,    Environment._onInternetAvailableIOError);
				internetAvailable_dataloader.addEventListener(LoaderEvent.FAIL,     Environment._onInternetAvailableIOError);
				internetAvailable_dataloader.addEventListener(LoaderEvent.COMPLETE, Environment._onInternetAvailableComplete);
				internetAvailable_dataloader.load();
			}
			
			return isInternetAvailable_boolean;
		}
		
		
		
		//PRIVATE	
		
		//--------------------------------------
		//  Event Handlers
		//--------------------------------------		
		/**
		 * Handles the event: <code>LoaderEvent.COMPLETE</code>.
		 * 
		 * @param event <code>LoaderEvent</code> The incoming event payload.
		 *  
		 * @return void
		 * 
		 */
		private static function _onInternetAvailableIOError(event : LoaderEvent) : void
		{
			Environment.isInitialized_boolean = true;
			getInstance().isInternetAvailable = false;
		}
		
		/**
		 * Handles the event: <code>LoaderEvent.HTTP_STATUS</code>.
		 * 
		 * @param event <code>LoaderEvent</code> The incoming event payload.
		 *  
		 * @return void
		 * 
		 */
		private static function _onInternetAvailableComplete(event : LoaderEvent) : void
		{
			
			//
			Environment.isInitialized_boolean = true;
			
			if (event.data == null) {
				
				//NOT LOADED PROPERTLY
				getInstance().isInternetAvailable = false;
				
			} else {
				getInstance().isInternetAvailable = true;
				
			}
		}
		
	}
}


//--------------------------------------
//  Singleton Enforcer: Prevents 
//	Instantiation of The Class 
//	Above From Anywhere Outside 
//	This Document
//--------------------------------------
internal class SingletonEnforcer {}